#include <iostream>
#include <unistd.h>//read,隐式包含了errno
#include <fcntl.h>//fcntl

//设置文件描述符阻塞
void SetNoBlock(int fd)
{
    int f1 = fcntl(fd, F_GETFL);//获取该文件描述符的标志位
    if(f1 < 0)
    {
        perror("fcntl error");
        return;
    }
    fcntl(fd, F_SETFL, f1 | O_NONBLOCK);//将要修改的标志位设置到内核
}
int main()
{
    SetNoBlock(0);
    char buffer[1024];
    while(true)
    {
        //从0号设备(键盘)中读取数据,到取到buffer,读取sizeof(buffer) - 1个
        ssize_t n = read(0, buffer, sizeof(buffer) - 1);
        if(n > 0)
        {
            //buffer[n] = 0;//添加结束位,这里会使我们的输出多一个空格,所以我们应该手动去掉,以前没有是因为我们使用C/C++的读取,读取时会给我们处理掉
            buffer[n - 1] = 0;//去除我们手动输入的空格,这里的n-1不会有越界的问题,因为只有n>0才可以进来,并且只有输入东西0号设备才会触发硬件中断,所以至少会进来一个字节(换行)
            //读取成功
            std::cout << buffer << std::endl;
        }
        else if(n < 0)
        {
            //因为我们以前用的都是阻塞型的I/O所以,所以我们收到数据时n<0一定是出错了
            //但我们今天使用的是非阻塞I/O,n < 0有两种情况,读取出错,数据没准备好
            //read返回值出错时,会返回－1,并且将错误码errno设置
            //errno被设置成EAGAIN或者EWOULDBLOCK时代表数据没准备好
            //设置为EINTR代表数据读取被信号中断了(非阻塞I/O般不会有这个问题,但还是建议写上)
            if(errno == EAGAIN || errno == EWOULDBLOCK)
            {
                //表示数据还没准备好
                std::cout << "数据还没准备好" << std::endl;
                sleep(1);
            }
            else if(errno == EINTR)
            {
                //读取中短重新读取
                continue;
            }
            else
            {
                //读取数据真的出错了
                break;
            }
        }
        else 
        {
            //n=0,表示对端（或内核）已经发来了 EOF——即字节流层面已结束，此时 fd 仍然有效
            std::cout << "~~~~~~~~~~" << std::endl;
            close(0);
            //break;
        }
        //std::cout << "......";//这里不输出的原因是因为没有刷新缓冲区

        // std::cout << "做自己的事" << std::endl;
        // sleep(1);
    }
    return 0;
}